package com.tanhua.server.service;

import com.tanhua.autoconfig.template.OssTemplate;
import com.tanhua.commons.Constants;
import com.tanhua.commons.exception.TanHuaException;
import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.dubbo.api.mongo.CommentApi;
import com.tanhua.dubbo.api.mongo.MovementApi;
import com.tanhua.dubbo.api.mongo.VisitorApi;
import com.tanhua.model.db.UserInfo;
import com.tanhua.model.enums.CommentType;
import com.tanhua.model.mongo.Comment;
import com.tanhua.model.mongo.Movement;
import com.tanhua.model.mongo.Visitors;
import com.tanhua.model.vo.*;
import com.tanhua.server.interceptor.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 圈子管理业务处理层
 */
@Service
@Slf4j
public class MovementService {
    @Autowired
    private OssTemplate ossTemplate;

    @DubboReference
    private MovementApi movementApi;

    @DubboReference
    private UserInfoApi userInfoApi;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @DubboReference
    private CommentApi commentApi;


    @DubboReference
    private VisitorApi visitorApi;
    /**
     * 动态-发布
     * imageContent：多张图片文件对象 将图片上传阿里云 获取图片地址后 存入mongodb数据
     * imageContent:微信使用这个可以为空
     */
    public void addMovement(MovementVo movementVo, MultipartFile[] imageContent) {
        try {
            List<String> medias = new ArrayList<>();
            //1将图片存入阿里云得到图片地址 List<String>集合
            if (imageContent != null && imageContent.length > 0) {
                for (MultipartFile multipartFile : imageContent) {
                    String originalFilename = multipartFile.getOriginalFilename();
                    //上传阿里云后图片地址
                    String imgUrl = ossTemplate.upload(originalFilename, multipartFile.getInputStream());
                    medias.add(imgUrl);
                }
            }
            //2构造Movement对象
            Movement movement = new Movement();
            BeanUtils.copyProperties(movementVo, movement);//动态文字 经纬度
            movement.setPid(11l);//推荐系统的自增id
            movement.setUserId(UserHolder.getUserId());//发布动态作者id
            movement.setLocationName(movementVo.getLocation());//中文地址名称
            movement.setMedias(medias);//图片地址信息已设置
            //3调用服务发布动态
            movementApi.add(movement);
        } catch (IOException e) {
            throw new TanHuaException(ErrorResult.error());
        }
    }

    /**
     * 我的动态
     * userId:为了当前此功能给其它地方使用
     */
    public PageResult<MovementsVo> findPageMovement(Long page, Long pagesize, Long userId) {
        log.debug("******page{}*********pagesize{}*********userId{}*********", page, pagesize, userId);
        if (userId == null) {
            userId = UserHolder.getUserId();//如果没有传此值 用当前用户id查询我的动态数据
        }
        //1调用服务根据用户id分页查询动态表数据
        PageResult<Movement> pageResult = movementApi.findPageMovement(page, pagesize, userId);
        if (pageResult == null || CollectionUtils.isEmpty(pageResult.getItems())) {
            return null;//没有发布过任务动态数据
        }
        //2调用服务根据用户id查询用户详情
        UserInfo userInfo = userInfoApi.findUserInfo(userId);
        log.debug("******userInfo{}***********", userInfo.toString());
        //3基于用户详情与动态表数据构造VO返回
        List<MovementsVo> movementsVoList = new ArrayList<>();
        List<Movement> movementList = pageResult.getItems();
        for (Movement movement : movementList) {
            movementsVoList.add(MovementsVo.init(userInfo, movement));
        }
        log.debug("******getCounts{}***********************movementsVoList{}***", pageResult.getCounts(), movementsVoList.toString());
        return new PageResult<>(page, pagesize, pageResult.getCounts(), movementsVoList);
    }

    /**
     * 好友动态
     */
    public PageResult<MovementsVo> findPageMovementByFriend(Long page, Long pagesize) {
        log.debug("******page{}*******pagesize{}*********",page,pagesize);
        Long loginUserId = UserHolder.getUserId();//当前登录用户id
        //1.调用服务分页查询动态表（服务中先查时间线再查动态表）
        PageResult<Movement> pageResult = movementApi.findPageMovementByFriend(page, pagesize, loginUserId);
        log.debug("******pageResult{}*********",pageResult.toString());
        //2.如果数据不存在 直接返回null
        if (pageResult == null || CollectionUtils.isEmpty(pageResult.getItems())) {
            return null;
        }
        return getMovementsVoPageResult(page, pagesize, pageResult);
    }

    /**
     * 好友动态 推荐动态公共方法
     * @param page
     * @param pagesize
     * @param pageResult
     * @return
     */
    private PageResult<MovementsVo> getMovementsVoPageResult(Long page, Long pagesize, PageResult<Movement> pageResult) {
        //3.如果数据存在 获取出所有动态作者ids
        List<Movement> movementList = pageResult.getItems();
        List<Long> movementUserIds = movementList.stream().map(Movement::getUserId).collect(Collectors.toList());
        log.debug("*********movementUserIds:::{}***************",movementUserIds);
        List<MovementsVo> movementsVoList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(movementUserIds)) {
            //4.根据动态作者ids查询作者详情
            Map<Long, UserInfo> userInfoMap = userInfoApi.findByCondition(movementUserIds, null);
            //5.基于动态数据以及用户详情 构造分页vo返回
            for (Movement movement : movementList) {
                UserInfo userInfo = userInfoMap.get(movement.getUserId());
                MovementsVo movementsVo = MovementsVo.init(userInfo, movement);
                //5.1根据key查询redis看当前用户是否对此动态已经点赞
                String key = Constants.MOVEMENTS_INTERACT_KEY + movement.getId().toHexString();//大Key 相当于表(针对某个动态操作 全部在这个key中)
                String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + UserHolder.getUserId(); //小key（针对动态操作 点赞、喜欢等）
                if(stringRedisTemplate.opsForHash().hasKey(key,hashKey)) {
                    movementsVo.setHasLiked(1);
                }
                //movementsVo.setHasLoved(1);
                movementsVoList.add(movementsVo);
            }
        }
        log.debug("******getCounts{}***********************movementsVoList{}***", pageResult.getCounts(), movementsVoList.toString());
        return new PageResult<>(page, pagesize, pageResult.getCounts(), movementsVoList);
    }

    /**
     * 推荐动态 control+alt+m（代码抽取快捷键）
     */
    public PageResult<MovementsVo> findPageMovementByRecommend(Long page, Long pageSize) {
        log.debug("*******findPageMovementByRecommend*****page{}********pageSize{}*********",page,pageSize);
        String key = Constants.MOVEMENTS_RECOMMEND + UserHolder.getUserId();
        //1根据key查redis中Pids(key不能随便改)
        String pidStrs = stringRedisTemplate.opsForValue().get(key); //pidStr以逗号分隔的 key=MOVEMENTS_RECOMMEND_1 value=1000个值
        log.debug("*******key*{}****pidStrs**{}********",key,pidStrs);
        //2如果没有值 构造随机查询10个动态数据
        PageResult<Movement> pageResult = new PageResult<>();
        if(StringUtils.isEmpty(pidStrs)){
            pageResult = movementApi.findPageMovementByRecommend();//实际场景下 应该很少触发
            pageResult.setCounts(10l);
            log.debug("******构造随机查询10个动态数据{}********",pageResult);
        }else {
            //3如果有值，根据Pids分页查询动态表数据
            //3.1 pidStr分页
            String[] recommendMovementIdsArr = StringUtils.split(pidStrs, ","); //1,2,3,4,5,.... 假设在有1000个Pids
            //String[] recommendMovementIdsArr = pidStr.split(",");
            // 计算分页的开始行
            long start = (page-1)*pageSize;//从哪条开始查询
            log.debug("******recommendMovementIdsArr{}*****start{}***",recommendMovementIdsArr,start);
            // 分页操作
            List<Long> pidList = Arrays.stream(recommendMovementIdsArr)
                    .skip(start).limit(pageSize).map(pidStr -> Long.valueOf(pidStr)).collect(Collectors.toList());
            log.debug("*********pidList{}**********",pidList);
            if(!CollectionUtils.isEmpty(pidList)) {
                pageResult = movementApi.findPageMovementByPids(pidList);
                //总记录数
                pageResult.setCounts((long) recommendMovementIdsArr.length);
            }
            log.debug("******大数据推荐的动态{}********",pageResult);
        }
        //4获取动态所有作者id调用服务获取用户详情
        //5.基于动态对象 用户详情对象构造分页VO返回
        return getMovementsVoPageResult(page, pageSize, pageResult);
    }

    /**
     * 单条动态
     * publishId:动态表主键id
     */
    public MovementsVo findMovement(String publishId) {
        //根据动态主键id调用服务查询动态对象
        Movement movement = movementApi.findMovement(publishId);
        UserInfo userInfo = userInfoApi.findUserInfo(movement.getUserId());
        return MovementsVo.init(userInfo, movement);
    }

    /**
     * 动态点赞
     */
    public Integer like(String publishId) {
        //1.调用服务保存点赞记录 动态评论表中
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(publishId));//动态id
        comment.setCommentType(CommentType.LIKE.getType());//评论类型，1-点赞，2-评论，3-喜欢
        comment.setUserId(UserHolder.getUserId());//评论人用户id
        Integer count = commentApi.add(comment);
        //2.将当前用户针对此动态的点赞记录写入redis(好友 推荐动态查询需要查redis)
        String key = Constants.MOVEMENTS_INTERACT_KEY + publishId;//大Key 相当于表(针对某个动态操作 全部在这个key中)
        String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + UserHolder.getUserId(); //小key（针对动态操作 点赞、喜欢等）
        stringRedisTemplate.opsForHash().put(key,hashKey,"1");
        return count;
    }


    /**
     * 动态取消点赞
     */
    public Integer dislike(String publishId) {
        //1.调用服务删除点赞记录 从动态评论表中
        Comment comment = new Comment();
        comment.setPublishId(new ObjectId(publishId));//动态id
        comment.setCommentType(CommentType.LIKE.getType());//评论类型，1-点赞，2-评论，3-喜欢
        comment.setUserId(UserHolder.getUserId());//评论人用户id
        Integer count = commentApi.delete(comment);
        //2.将当前用户针对此动态的点赞记录从redis删除
        String key = Constants.MOVEMENTS_INTERACT_KEY + publishId;//大Key 相当于表(针对某个动态操作 全部在这个key中)
        String hashKey = Constants.MOVEMENT_LIKE_HASHKEY + UserHolder.getUserId(); //小key（针对动态操作 点赞、喜欢等）
        stringRedisTemplate.opsForHash().delete(key,hashKey);
        return count;
    }

    /**
     * 谁看过我
     *  1. 读取谁看过消息时间 在哪里操作存入redis的时间？
     *  答：首页 点击进入谁看我分页查询-记录读取消息时间（没有实现）
     *     以hash方式来存储
     *  2.为什么要用Hash的方式拿到timeStr？
     *  答：点击头像进入分页查询，会记录消息读取时间。根据这个时间作为条件查询
     * @return
     */
    public List<VisitorsVo> findVisitors() {
        Long loginUserId = UserHolder.getUserId();//当前登录用户id
        //redis的key, hash 所有用户的访客时间都记录这个hash集合里
        String key = Constants.VISITORS_USER;
        // whoKey,登录用户id
        String whoKey = UserHolder.getUserId().toString();
        //1根据当前用户id查询redis当前用户读取消息时间 （redis存入时间是string类型）
        String timeStr = (String)stringRedisTemplate.opsForHash().get(key,whoKey);
        Long timeLong = timeStr == null ? null : Long.valueOf(timeStr);
        log.debug("******timeLong*******",timeLong);
        //2调用服务获取最近访客集合数据Visitors
        List<Visitors> visitorsList = visitorApi.findVisitors(timeLong,loginUserId);
        log.debug("******visitorsList*******",visitorsList);
        if(CollectionUtils.isEmpty(visitorsList)){
            return null;
        }
        //3获取所有访客用户ids
        List<Long> visitorUserIds = visitorsList.stream().map(Visitors::getVisitorUserId).collect(Collectors.toList());
        log.debug("******visitorUserIds*******",visitorUserIds);
        //4根据所有访客用户ids查询tb_userInfo详情
        Map<Long, UserInfo> userInfoMap = userInfoApi.findByCondition(visitorUserIds, null);
        //5.基于tb_userInfo和Visitors构造Vo返回
        List<VisitorsVo> visitorsVoList = new ArrayList<>();
        for (Visitors visitors : visitorsList) {
            UserInfo userInfo = userInfoMap.get(visitors.getVisitorUserId());
            visitorsVoList.add(VisitorsVo.init(userInfo,visitors));
        }
        log.debug("******visitorsVoList*******",visitorsVoList);
        return visitorsVoList;
    }
}
